import {dynamicObj} from "../types";
import {isNil} from "ramda";
import {findLeaf} from "./Tree";
/**
 * 读写扩展，将扩展getter，setter方法，更易于双向绑定的使用
 */
const ReservedFunction: dynamicObj = {
    /**
     * 取值时解析JSON字符串为JS对象，赋值后将JS对象序列化回JSON字符串
     */
    "JSON": {
        get: (v: string) => {
            try {
                return JSON.parse(v)
            }catch (e) {
                console.error(e)
                return v
            }
        },
        set: (tempSchema: dynamicObj, tempKey: string, tempValue: any) => {
            try {
                tempSchema[tempKey] = JSON.stringify(tempValue)
                return true
            }catch (e) {
                console.error(e)
                tempSchema[tempKey] = '{}'
                return true
            }
        }
    },
    /**
     * 取值时如果为空则创建空数组容器，赋值前如果没有数组容器，则创建数组容器
     */
    "Array": {
        get: (v: string, tempSchema: dynamicObj, tempKey: string) => {
            if(!v) tempSchema[tempKey] = []
            return tempSchema[tempKey]
        },
        preSet: (tempSchema: dynamicObj, tempKey: string, tempValue: any) => {
            if(!tempSchema) tempSchema = []
            for(let i = 0; i < Number(tempKey); i++){
                if(isNil(tempSchema[i])) {
                    tempSchema[i] = null
                }
            }
            return tempSchema
        }
    },
    /**
     * 取值时如果上级是对象数组，则调用Array.property.map(item => item[index])
     * 赋值时则调用Array.property.forEach(item => item[index] = tempValue)
     */
    "Item": {
        get: (v: string, tempSchema: dynamicObj, tempKey: string) => {
            if(Array.isArray(tempSchema)) {
                return tempSchema.map(item => item[tempKey])
            }else {
                return tempSchema[tempKey]
            }
        },
        set: (tempSchema: dynamicObj, tempKey: string, tempValue: any) => {
            for(let i = 0; i < tempValue.length; i++){
                if(!isNil(tempValue[i])) {
                    if(!tempSchema[i]) tempSchema[i] = {}
                    tempSchema[i][tempKey] = tempValue[i]
                }
            }
            return tempSchema
        }
    },
    /*针对数字与数组 todo 切换A2S*/
    "ItemString": {
        get: (v: string, tempSchema: dynamicObj, tempKey: string) => {
            if(Array.isArray(tempSchema)) {
                return tempSchema.map(item => item[tempKey]).toString()
            }else {
                return tempSchema[tempKey]
            }
        },
        set: (tempSchema: dynamicObj, tempKey: string, tempValue: any) => {
            tempValue = tempValue.split(',')
            for(let i = 0; i < tempValue.length; i++){
                if(!tempSchema[i]) tempSchema[i] = {}
                if(!isNil(tempValue[i])) {
                    if(!tempSchema[i]) tempSchema[i] = {}
                    tempSchema[i][tempKey] = tempValue[i]
                }
            }
            return tempSchema
        }
    },
    /*针对数字与数组 todo 切换N2S*/
    "String": {
        get: (v: string) => {
            return v.toString()
        },
        set: (tempSchema: dynamicObj, tempKey: string, tempValue: any) => {
            if(typeof tempValue !== 'string') {
                tempSchema[tempKey] = tempValue.toString()
            }else {
                tempSchema[tempKey] = tempValue
            }
            return tempSchema
        }
    },
    "Number": {
        get: (v: string) => {
            return Number(v || 0)
        },
        set: (tempSchema: dynamicObj, tempKey: string, tempValue: any) => {
            tempSchema[tempKey] = Number(tempValue || 0)
            return tempSchema
        }
    },
    "Boolean": {
        get: (v: string) => {
            if(v === 'true') return true
            if(v === 'false') return false
            return !!v
        },
        set: (tempSchema: dynamicObj, tempKey: string, tempValue: any) => {
            if(tempValue === 'true') tempSchema[tempKey] = true
            else if(tempValue === 'false') tempSchema[tempKey] = false
            else tempSchema[tempKey] = !!tempValue
            return tempSchema
        }
    },
    /**
     * 取一叉树叶子 todo 赋值未完成
     */
    "Leaf": {
        get: (v: string, tempSchema: dynamicObj, tempKey: string) => {
            return findLeaf(tempSchema, tempKey)
        }
    }
}

/**
 * 获取符合openApi中$Ref格式的内容
 * @param {Object} target
 * @param {String} refName
 * @param {String} splitString
 */
export function queryDefinitionByRef(target: object, refName: string, splitString: string = '/') {
    let refArray = refName?.split(splitString)
    refArray.shift() // 抛出"#"
    let tempSchema: dynamicObj = target // 临时参数(每次循环体执行后会改变)，当前内容，随着逐步深入解析改变
    while(refArray.length > 0) {
        // todo 是否对Map与Set提供支持，待决定
        if(!tempSchema) return tempSchema // 开始时做非空判断，防止出现空指针
        let tempKey = refArray.shift() || '' // 临时参数，取当前key或index
        if(tempKey === '') return tempSchema // 应对refName为#/的情况
        let regexRes = /\$([a-zA-Z0-9]+)_(.+)/.exec(tempKey) // 当识别到 $ 开始 _ 结束的正则，开始匹配保留字，做对应处理
        if(regexRes) {
            const [matchString, reservedWord, newTempKey] = regexRes
            tempKey = newTempKey
            let reversed = ReservedFunction[reservedWord]
            if(reversed && reversed.get) {
                tempSchema = reversed.get(tempSchema[tempKey], tempSchema, tempKey)
                continue
            }
        }
        tempSchema = tempSchema[tempKey]
    }
    return tempSchema
}

/**
 * 设置符合openApi中$Ref格式的内容
 * @param {Object} target
 * @param {String} refName
 * @param {any} value
 * @param {String} splitString
 */
export function setDefinitionByRef(target: object|[], refName: string, value: any, splitString: string = '/') {
    let refArray = refName?.split(splitString)
    // refArray.shift() // 不抛出"#"
    let tempSchema: dynamicObj;
    let tempValue = value;
    let tempKey: string = refArray.pop() || '';
    let preKey: string;
    while (refArray.length > 0) {
        let tempRef = refArray.join('/')
        tempSchema = queryDefinitionByRef(target, tempRef) // 取当前要赋值的区域
        preKey = refArray[refArray.length-1] // 赋值区域的键名
        let regexResPre = /\$([a-zA-Z0-9]+)_(.+)/.exec(preKey) // 当识别到 $ 开始 _ 结束的正则，开始匹配保留字，做对应处理
        let regexResTemp = /\$([a-zA-Z0-9]+)_(.+)/.exec(tempKey) // 当识别到 $ 开始 _ 结束的正则，开始匹配保留字，做对应处理
        if(regexResPre) {
            const [ms, $1, $2] = regexResPre
            let reversed = ReservedFunction[$1]
            if(reversed && reversed.preSet) {
                tempSchema = reversed.preSet(tempSchema, tempKey, tempValue)
            }
        }
        if(regexResTemp) {
            const [ms, $1, $2] = regexResTemp
            let reversed = ReservedFunction[$1]
            if(reversed && reversed.set) {
                tempValue = reversed.set(tempSchema, $2, tempValue)
            }else {
                if(!tempSchema) tempSchema = {}
                tempSchema[$2] = tempValue
                tempValue = tempSchema
            }
        }else {
            if(!tempSchema) tempSchema = {}
            tempSchema[tempKey] = tempValue
            tempValue = tempSchema
        }
        tempKey = preKey;
        refArray.pop();
    }
    return true
}

/**
 * 读写代理内容
 */
const handler: ProxyHandler<any> = {
    get: (target: dynamicObj, key: string): any => {
        let result;
        if(typeof key === 'string' && key.includes('#/')) {
            result = queryDefinitionByRef(target, key)
        }else {
            result = target[key]
        }
        return result
    },
    set: (target: dynamicObj, key: string, value: any): boolean => {
        if(typeof key === 'string') {
            if(key.includes('#/')) {
                setDefinitionByRef(target, key, value)
                return true
            }
        }
        target[key] = value
        return  true
    }
}

/**
 * 读写代理内容
 */
const deepHandler: ProxyHandler<any> = {
    get: (target: dynamicObj, key: string): any => {
        let result;
        if(typeof key === 'string' && key.includes('#/')) {
            result = queryDefinitionByRef(target, key)
        }else {
            result = target[key]
        }
        if(['[object Object]', '[object Array]'].includes(Object.prototype.toString.call(result))) {
            return new Proxy(result, deepHandler)
        }else {
            return result
        }
    },
    set: (target: dynamicObj, key: string, value: any): boolean => {
        if(typeof key === 'string') {
            if(key.includes('#/')) {
                setDefinitionByRef(target, key, value)
                return true
            }
        }
        target[key] = value
        return  true
    }
}


/**
 * 构造函数，创建一个智能读写对象
 * @constructor
 */
export function shallowRW(data: object|[]) {
    return new Proxy(data, handler)
}

export function RW(data: object|[]) {
    if(isNil(data)) {
        return {}
    }
    return new Proxy(data, deepHandler)
}

export default {
    RW, shallowRW
}
